﻿using Heng.Elements.Core.IOC;
using Heng.Elements.Core.Model.Infrastructure.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Heng.Elements.Core.Model.Infrastructure.IOC;

namespace Heng.Elements.Core.Model.Infrastructure.Repositories
{
    /// <summary>
    /// Entity repository context 
    /// </summary>
    public class EntityRepositoryContext : IEntityRepositoryContext
    {
        #region Properties
        // current repository context
        IEntityRepositoryContext _context = null;
        #endregion

        /// <summary>
        /// Get repository for a specific entity.
        /// </summary>
        /// <typeparam name="TEntity"> Entity type. </typeparam>
        /// <returns> Entity repository. </returns>
        public IEntityRepository<TEntity> GetEntityRepository<TEntity>()
            where TEntity : class, IEntity
        {
            return GetRepository<IEntityRepository<TEntity>>();
        }

        /// <summary>
        /// Get repository.
        /// </summary>
        /// <typeparam name="TEntityRepository"> Repository type. </typeparam>
        /// <returns> Repository. </returns>
        public TEntityRepository GetRepository<TEntityRepository>()
            where TEntityRepository : IEntityRepository
        {
            // get repository from IOC
            TEntityRepository repo = Ioc.Instance.GetEntityRepositoryByType<TEntityRepository>();

            // check if instance was obtained
            if (repo == null)
                throw new NullReferenceException(string.Format("Could not create repository instance of type '{0}'.", typeof(TEntityRepository)));

            // check if context was created
            if (repo.RepositoryContext == null)
                throw new NullReferenceException(string.Format("Repository instance of type '{0}' does not have an associated repository context.", repo.GetType()));

            // if there is no current context then set repository instance context as current else use current context for all subsequent repository instances
            if (_context == null)
            {
                // set current repository context
                _context = repo.RepositoryContext;
            }
            else
            {
                // check if current repository context is compatible with repository instance context
                if (_context.GetType() != repo.RepositoryContext.GetType())
                {
                    // source and target repository contexts are not compatible
                    throw new InvalidOperationException(string.Format("Current repository context is of type '{0}' and is not compatible with type '{1}'.", _context.GetType(), repo.RepositoryContext.GetType()));
                }
                else
                {
                    // assign current repository context because source and target repository contexts are compatible
                    repo.RepositoryContext = _context;
                }
            }

            return repo;
        }

        /// <summary>
        /// Save all changes in current context.
        /// </summary>
        public void SaveChanges()
        {
            if (_context != null)
                _context.SaveChanges();
        }

        /// <summary>
        /// Dispose current context.
        /// </summary>
        public void Dispose()
        {
            if (_context != null)
                _context.Dispose();
        }
    }
}
